﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SKYPE4COMLib;
using System.Runtime.InteropServices;
using System.Speech.Recognition;
using System.Windows.Threading;
using System.Threading;
using System.IO;

namespace SmartVoice
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>

    public partial class MainWindow : Window
    {
        SkypeLink skype = new SkypeLink();
        VoiceSynth voiceSynth = new VoiceSynth();
        VoiceRecognizer voiceRecognizer = new VoiceRecognizer();
        GPS gps = new GPS();
        DispatcherTimer contactsTimer = new DispatcherTimer();
        DispatcherTimer callTimer = new DispatcherTimer();

        //voice commands
        List<String> messageCommands = new List<String>();
        List<String> statusOptions = new List<String>();
        List<String> voiceCommands = new List<String>();

        //contacts containers
        List<String> contactsList = new List<String>();
        List<String> contactNames = new List<String>();
        List<IUser> auxContacts;

        //messages
        List<IChatMessage> unreadMessages = new List<IChatMessage>();
        String messageToSend;
        String messageContact;

        //general stuff
        String clientName;
        Call activeCall;
        int confirmMessage = 0;
        int mode = 0;
        int activeCmd = -1;

        ICallCollection missedCalls;

        enum VoiceCommands
        {
            Call, CallMissedContact, ReadLastMessages, WriteMessage,
            ChangeStatus, AddContact, RemoveContact, RejectCall, AnswerCall, ConfirmMessage,
            RejectMessage
        };

        enum StatusOptions { Online, Away, Busy, Driving };

        enum Mode { StandBy, RequestContactID, WritingMessage, ChangingStatus }




        public MainWindow()
        {
            InitializeComponent();

            //timer for contact updates
            contactsTimer.Tick += new EventHandler(UpdateContacts);
            contactsTimer.Interval = new TimeSpan(0, 0, 5);

            //timer for call updates
            callTimer.Tick += new EventHandler(UpdateCallInfo);
            callTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);


            //get client name and skype status
            clientName = skype.skype.CurrentUserHandle.ToString();
            skypeStatusBox.Text = skype.skype.CurrentUserStatus.ToString().Substring(3);

            //skype status options
            statusOptions.Add("online");
            statusOptions.Add("away");
            statusOptions.Add("busy");
            statusOptions.Add("driving");

            //setup voice commands
            voiceCommands.Add("call");
            voiceCommands.Add("call missed contact");
            voiceCommands.Add("reject call");
            voiceCommands.Add("answer call");
            voiceCommands.Add("read last messages");
            voiceCommands.Add("write message");
            voiceCommands.Add("change status");
            voiceCommands.Add("update contacts");
            voiceCommands.Add("add contact");
            voiceCommands.Add("remove contact");
            voiceRecognizer.SetGrammar(voiceCommands);

            //setup message commands
            messageCommands.Add("confirm message");
            messageCommands.Add("reject message");

            //display contact list            
            DisplayContactList();

            //set event handlers            
            voiceRecognizer.speechReco.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(voiceRecognized);
            voiceRecognizer.speechReco.SpeechRecognitionRejected += new EventHandler<SpeechRecognitionRejectedEventArgs>(voiceNotRecognized);
            ((_ISkypeEvents_Event)skype.skype).CallStatus += BusyWithCall;
            ((_ISkypeEvents_Event)skype.skype).MessageStatus += MessageHandler;

            //start automatic contact updater
            contactsTimer.Start();

            //start recognizing voice
            voiceRecognizer.RecognizeAsync();
        }

        [PreEmptive.Attributes.Feature("VoiceNotRecognized")]
        public void voiceNotRecognized(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            resetAll(1);
        }

        [PreEmptive.Attributes.Feature("VoiceRecognized")]
        public void voiceRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            //if there is an active call, check for "cancel call" or "answer call"
            if (skype.skype.ActiveCalls.Count != 0)
            {
                int cmdNumber = parseCommands(e.Result.Text);
                switch (cmdNumber)
                {
                    case (int)VoiceCommands.RejectCall:
                        try
                        {
                            activeCall.Finish();
                            voiceSynth.speakText("Call canceled");
                        }
                        catch (Exception) { }
                        resetAll(0);
                        break;

                    case (int)VoiceCommands.AnswerCall:
                        try
                        {
                            activeCall.Answer();
                        }
                        catch (Exception) { }
                        resetAll(0);
                        break;

                    default:
                        break;
                }
            }
            else if (mode == (int)Mode.StandBy)  //waiting for commands
            {
                int cmdNumber = parseCommands(e.Result.Text);
                switch (cmdNumber)
                {
                    case (int)VoiceCommands.ChangeStatus:
                        appStatsBox.Text = "Online,Busy,Driving,Away";
                        mode = (int)Mode.ChangingStatus; activeCmd = (int)VoiceCommands.ChangeStatus;
                        voiceRecognizer.UnloadAllGrammars();
                        voiceRecognizer.SetGrammar(statusOptions);
                        break;

                    case (int)VoiceCommands.WriteMessage:
                        mode = (int)Mode.RequestContactID; activeCmd = (int)VoiceCommands.WriteMessage;
                        appStatsBox.Text = "Message to which contact?";
                        voiceRecognizer.SetGrammar(contactsList);
                        voiceSynth.speakText("Which contact?");
                        break;

                    case (int)VoiceCommands.Call:
                        mode = (int)Mode.RequestContactID; activeCmd = (int)VoiceCommands.Call;
                        appStatsBox.Text = "Call which contact?";
                        voiceRecognizer.UnloadAllGrammars();
                        voiceRecognizer.SetGrammar(contactsList);
                        voiceSynth.speakText("Which contact?");
                        break;

                    case (int)VoiceCommands.CallMissedContact:
                        missedCalls = skype.skype.MissedCalls;
                        if (missedCalls.Count == 0)
                        {
                            voiceSynth.speakText("No missed calls");
                        }
                        else
                        {
                            skype.skype.PlaceCall(missedCalls[0].PartnerHandle.ToString(), "", "", "");
                        }
                        resetAll(0);
                        break;

                    case (int)VoiceCommands.ReadLastMessages:
                        if (unreadMessages.Count == 0) voiceSynth.speakText("There are no unread messages");
                        else
                        {
                            for (int i = 0; i < unreadMessages.Count; i++)
                            {
                                voiceSynth.speakText("Message from" + unreadMessages[i].FromHandle);
                                voiceSynth.speakText(unreadMessages[i].Body);
                            }
                            unreadMessages.Clear();
                        }
                        resetAll(0);
                        break;

                    default:
                        resetAll(1);
                        break;
                }
            }
            else if (mode == (int)Mode.RequestContactID) //waiting for contact id
            {
                int validate = parseContacts(e.Result.Text); //bug with contacts, gotta check for commands
                if (validate == -1) resetAll(2);
                else
                {
                    switch (activeCmd)
                    {
                        case (int)VoiceCommands.Call:  //call
                            voiceSynth.speakText("Calling" + e.Result.Text);
                            skype.skype.PlaceCall(e.Result.Text, "", "", "");
                            resetAll(0);
                            break;

                        case (int)VoiceCommands.WriteMessage: //write message
                            messageContact = e.Result.Text;
                            messageBoxInfo.Text = "Message to " + e.Result.Text;
                            appStatsBox.Text = "Dictating message";
                            mode = (int)Mode.WritingMessage;
                            voiceRecognizer.UnloadAllGrammars();
                            voiceRecognizer.SetGlobalGrammar();
                            voiceRecognizer.SetGrammar(messageCommands);
                            voiceSynth.speakText("Please start dictating");
                            break;

                        default:
                            resetAll(1);
                            break;
                    }
                }
            }
            else if (mode == (int)Mode.WritingMessage) //write message
            {
                int cmdNumber = parseCommands(e.Result.Text);
                switch (cmdNumber)
                {
                    case (int)VoiceCommands.RejectMessage:
                        voiceSynth.speakText("Message discarded");
                        messageContact = null;
                        messageToSend = null;
                        confirmMessage = 0;
                        messageBoxBody.Text = "";
                        messageBoxInfo.Text = "";
                        resetAll(0);
                        break;

                    case (int)VoiceCommands.ConfirmMessage: //confirm message
                        if (confirmMessage == 0) //check for message confirmation
                        {
                            voiceSynth.speakText("Message to" + messageContact);
                            voiceSynth.speakText(messageToSend);
                            voiceSynth.speakText("Please confirm");
                            confirmMessage = 1;
                        }
                        else //send message
                        {
                            skype.skype.CreateChatWith(messageContact);
                            skype.skype.SendMessage(messageContact, messageToSend);
                            voiceSynth.speakText("Message sent");
                            messageContact = null;
                            messageToSend = null;
                            confirmMessage = 0;
                            appStatsBox.Text = "Standby";
                            resetAll(0);
                        }
                        break;

                    default: //get message text
                        confirmMessage = 0;
                        messageToSend += " " + e.Result.Text;
                        messageBoxBody.Text = "";
                        messageBoxBody.Text = messageToSend;
                        break;
                }

            }
            else if (mode == (int)Mode.ChangingStatus) //Change Status
            {
                int status = parseStatus(e.Result.Text);
                switch (status)
                {
                    case (int)StatusOptions.Online:
                        skype.skype.CurrentUserStatus = TUserStatus.cusOnline;
                        break;

                    case (int)StatusOptions.Away:
                        skype.skype.CurrentUserStatus = TUserStatus.cusAway;
                        break;

                    case (int)StatusOptions.Busy:
                        skype.skype.CurrentUserStatus = TUserStatus.cusDoNotDisturb;
                        break;

                    case (int)StatusOptions.Driving:
                        skype.skype.CurrentUserStatus = TUserStatus.cusDoNotDisturb;
                        skype.skype.CurrentUserProfile.MoodText = "Driving";
                        voiceSynth.speakText("Changed mood to driving");
                        break;
                }
                skypeStatusBox.Text = skype.skype.CurrentUserStatus.ToString().Substring(3);
                voiceSynth.speakText("Changed status to" + skype.skype.CurrentUserStatus.ToString().Substring(3));
                resetAll(0);
            }
        }

        public int parseContacts(String contact)
        {
            if (contact.Equals("call") || contact.Equals("reject call") || contact.Equals("answer call") ||
                contact.Equals("read last messages") || contact.Equals("confirm message") || contact.Equals("reject message")
                || contact.Equals("call missed contact") || contact.Equals("online") || contact.Equals("busy") ||
                contact.Equals("driving") || contact.Equals("away"))
            {
                return -1;
            }
            return 0;

        }

        public int parseStatus(String status)
        {
            if (status.Equals("online")) return (int)StatusOptions.Online;
            if (status.Equals("away")) return (int)StatusOptions.Away;
            if (status.Equals("busy")) return (int)StatusOptions.Busy;
            if (status.Equals("driving")) return (int)StatusOptions.Driving;
            return -1;
        }

        public void resetAll(int i)
        {
            appStatsBox.Text = "Standby";
            mode = (int)Mode.StandBy;
            activeCmd = -1;
            if (i == 1) voiceSynth.speakText("Cannot comply");
            else if (i == 2) voiceSynth.speakText("Contact not recognized");
            voiceRecognizer.UnloadAllGrammars();
            voiceRecognizer.SetGrammar(voiceCommands);
        }

        public int parseCommands(String cmd)
        {
            if (cmd.Equals("call")) return (int)VoiceCommands.Call;
            if (cmd.Equals("call missed contact")) return (int)VoiceCommands.CallMissedContact;
            if (cmd.Equals("read last messages")) return (int)VoiceCommands.ReadLastMessages;
            if (cmd.Equals("write message")) return (int)VoiceCommands.WriteMessage;
            if (cmd.Equals("change status")) return (int)VoiceCommands.ChangeStatus;
            if (cmd.Equals("add contact")) return (int)VoiceCommands.AddContact;
            if (cmd.Equals("remove contact")) return (int)VoiceCommands.RemoveContact;
            if (cmd.Equals("reject call")) return (int)VoiceCommands.RejectCall;
            if (cmd.Equals("answer call")) return (int)VoiceCommands.AnswerCall;
            if (cmd.Equals("confirm message")) return (int)VoiceCommands.ConfirmMessage;
            if (cmd.Equals("reject message")) return (int)VoiceCommands.RejectMessage;
            return -1;
        }

        //timer event
        public void UpdateContacts(object source, EventArgs e)
        {
            DisplayContactList();
        }

        public void UpdateCallInfo(object source, EventArgs e)
        {
            try
            {
                if (callTimer.IsEnabled == true)
                {
                    callInfoBox.Text = "Calling -> " + activeCall.PartnerHandle.ToString() + "\r\n"
                    + "Duration (s)-> " + activeCall.Duration.ToString() + "\r\n"
                    + "Cost -> " + activeCall.Rate.ToString();
                }
            }
            catch (Exception) { };
        }

        public void DisplayContactList()
        {
            contactsBox.Clear();
            contactNames.Clear();
            contactsList.Clear();
            auxContacts = skype.GetAllContacts();
            for (int i = 0; i < auxContacts.Count; i++)
            {
                contactsList.Add(auxContacts[i].Handle);
            }


            //get unique handles and their positions
            try
            {
                for (int i = 0; i < contactsList.Capacity; i++)
                {
                    if (!contactNames.Contains(contactsList[i]))
                    {
                        contactNames.Add(contactsList[i]);

                        double[] pos2 = getPosition(contactsList[i]);
                        double[] pos = getOwnPosition();
                        if (pos[0] == 0 || pos[1] == 0 || pos2[0] == 0 || pos2[1] == 0)
                        {
                            contactsBox.AppendText(contactsList[i] + "  n/a " + "\r\n");
                        }
                        else
                        {
                            double distance = gps.DistanceBetweenTwoPoints(pos[0], pos[1], pos2[0], pos2[1]);
                            distance = Math.Round(distance, 3);
                            contactsBox.AppendText(contactsList[i] + "   " + distance.ToString() + " km" + "\r\n");
                        }

                    }
                }
            }
            catch (Exception) { }
        }

        public void BusyWithCall(Call call, TCallStatus status)
        {
            if (skype.skype.ActiveCalls.Count != 0) //get call
            {
                resetAll(0);
                activeCall = call;
                appStatsBox.Text = "Busy with Call";
                callTimer.Start();
            }
            else
            {
                callTimer.Stop();
                appStatsBox.Text = "Standby";
                resetAll(0);
                callInfoBox.Text = "Last call -> " + call.PartnerHandle.ToString() + "\r\n"
                + "Duration (s)-> " + call.Duration.ToString() + "\r\n"
                + "Cost -> " + call.Rate.ToString();
            }
        }

        public void MessageHandler(IChatMessage message, TChatMessageStatus status)
        {
            if (!message.FromHandle.ToString().Equals(clientName)) //check if message is from the skype client
            {
                if (appStatsBox.Text.Equals("Standby")) //if not busy, speak message
                {
                    voiceSynth.speakText("Message from" + message.FromHandle);
                    voiceSynth.speakText(message.Body);
                }
                else unreadMessages.Add(message); //if busy, save the message for later reading
                messageBoxInfo.Text = "Message from " + message.FromHandle + " " + message.Timestamp.ToString();
                messageBoxBody.Text = message.Body;
            }
            else //own message
            {
                messageBoxInfo.Text = "Message to " + message.Users[1].Handle + " " + message.Timestamp.ToString();
                messageBoxBody.Text = message.Body;
            }
        }

        public double[] getOwnPosition() //get gps position from skype client
        {
            double[] position = new double[2];
            try
            {
                //parse mood for position
                string mood = skype.skype.CurrentUserProfile.MoodText;
                string lat, lon;
                lat = mood.Split('#')[1];
                lon = mood.Split('#')[2];
                position[0] = Convert.ToDouble(lat);
                position[1] = Convert.ToDouble(lon);
                return position;
            }
            catch (Exception) //position not available
            {
                position[0] = 0;
                position[1] = 0;
                return position;
            }
        }

        public double[] getPosition(string contact) //get gps position from a contact
        {
            double[] position = new double[2];
            try
            {
                //parse mood for position
                string mood = skype.skype.get_User(contact).MoodText;
                string lat, lon;
                lat = mood.Split('#')[1];
                lon = mood.Split('#')[2];
                position[0] = Convert.ToDouble(lat);
                position[1] = Convert.ToDouble(lon);
                return position;
            }
            catch (Exception) //position not available
            {
                position[0] = 0;
                position[1] = 0;
                return position;
            }
        }
    }
}